﻿#define _CRT_SECURE_NO_WARNINGS

#include<stdio.h>
#include<stdlib.h>
#include<string.h>


//malloc函数，开辟大小单位为字节
//free函数，释放空间
//void* malloc(size_t size);
//malloc定义类型为void*，返回什么类型自己定义
//int main()
//{
//	int* p = (int*)malloc(5 * sizeof(int));//(int*)malloc(20)
//	if (p == NULL)
//	{
//		perror("malloc");
//		return 1;//整个函数直接结束
//	}
//	int i = 0;
//	for (i = 0; i < 5; i++)
//	{
//		*(p + i) = i;
//
//
//		//*p = i;
//		//p++;
//		//这样进行下去下面释放的就不是最初的空间了
//	}
//
//	free(p);//释放内存，传递给free函数的是要释放的内存空间的起始地址
//	p = NULL;//将释放的内存设置为空
//
//	return 0;
//}



//calloc，功能与malloc类似，都是向内存申请空间
//void* calloc (size_t num, size_t size);
//一个数量，一个类型
//int main()
//{
//	int* p1 = (int*)calloc(5, sizeof(int));//calloc会在传入指针之前将申请的空间的每个字节初始化为0
//	int* p2 = (int*)malloc(5 * sizeof(int));//malloc不会初始化所申请的内存空间
//
//	if (p1 == NULL || p2 == NULL)
//	{
//		perror("1");
//		return 1;
//	}
//
//
//	int i = 0;
//	for (i = 0; i < 5; i++)
//	{
//		*(p1 + i) = i;
//	}
//	for (i = 0; i < 5; i++)
//	{
//		*(p2 + i) = i;
//	}
//	for (i = 0; i < 5; i++)
//	{
//		printf("%d ", *(p1 + i));
//	}
//	for (i = 0; i < 5; i++)
//	{
//		printf("%d ", *(p2 + i));
//	}	
//
//	free(p1);
//	p1 = NULL;
//	free(p2);
//	p2 = NULL;
//
//	return 0;
//}


//realloc，可以做到对动态开辟内存⼤⼩的调整
//void* realloc(void* ptr, size_t size);
//ptr指针，size调整之后的内存大小(单位字节)
//返回值为调整之后的内存起始位置，在调整原内存空间⼤⼩的基础上，还会将原来内存中的数据移动到新的空间
	//情况1：原有空间之后有⾜够⼤的空间
	//情况2：原有空间之后没有⾜够⼤的空间
	//当是情况1 的时候，要扩展内存就直接原有内存之后直接追加空间，原来空间的数据不发⽣变化。
	//当是情况2 的时候，原有空间之后没有⾜够多的空间时，扩展的⽅法是：在堆空间上另找⼀个合适⼤⼩
	//的连续空间来使⽤。这样函数返回的是⼀个新的内存地址
	// 
	//总的来说后面空间够时，返回还是原地址，数据不移位置
	//不够时，返回新地址，数据一同移过去 
//int main()
//{
//	int* p = (int*)calloc(5, sizeof(int));//0 0 0 0 0
//
//	if (p == NULL)
//	{
//		perror("calloc");
//		free(p);
//		p = NULL;
//		return 1;
//	}
//	int i = 0;
//	for (i = 0; i < 5; i++)
//	{
//		*(p + i) = i;//0 1 2 3 4
//	}
//
//	int* ptr = realloc(p, 200);
//	if (ptr == NULL)
//	{
//		printf("开辟失败");//原有空间之后没有⾜够⼤的空间，会返回空指针
//	}
//	else
//	{
//		for (i = 5; i < 50; i++)
//		{
//			*(ptr + i) = i;//返回的ptr的地址与原来的p的地址不同了
//		}
//		for (i = 0; i < 50; i++)
//		{
//			printf("%d ", *(ptr + i));
//		}
//	}
//
//	printf("\np   = %p", p);
//	printf("\nptr = %p", ptr);
//
//	free(ptr);
//	ptr = NULL;
// 
//	return 0;
//}
//此外，realloc还可以实现malloc的功能
//int main()
//{
//	int* p = (int*)realloc(NULL, 20);//等价下面这个
//	int* p = (int*)malloc(20);
//	return 0;
//}
//不要忘了释放内存，自己开辟的动态内存，使用完后不要忘了释放(free)，以免占用内存




//常见的动态内存的错误
//1.对NULL指针的解引⽤操作
//void test()
//{
//	int* p = (int*)malloc(INT_MAX / 4);
//	*p = 20;//如果p的值是NULL，就会有问题
//	free(p);
//}
//需要判断下返回的p是否为空指针，因为开辟不出时会返回空指针
//而对空指针进行解引用操作，会有问题
//2.对动态开辟空间的越界访问
//void test()
//{
//	int i = 0;
//	int* p = (int*)malloc(10 * sizeof(int));//40
//	if (NULL == p)
//	{
//		exit(EXIT_FAILURE);
//	}
//	for (i = 0; i <= 10; i++)
//	{
//		*(p + i) = i;//当i是10的时候越界访问
//	}
//	free(p);
//}//理解，过
//3.对⾮动态开辟内存使⽤free释放
//void test()
//{
//	int a = 10;
//	int* p = &a;
//	free(p);//ok?flase，注释掉之后编译器不会报错
//	//free释放非动态开辟的空间编译器会报错
//}
//4.使⽤free释放⼀块动态开辟内存的⼀部分
//void test()
//{
//	int* p = (int*)malloc(100);
//	p++;//注释掉之后编译器不会报错
//	//说明这样使用error
//	free(p);//p不再指向动态内存的起始位置
//	//编译器会报错
//}
//5.对同⼀块动态内存多次释放
//void test()
//{
//	int* p = (int*)malloc(100);
//	free(p);
//	free(p);//重复释放
//	//对已释放的空间再次释放编译器会报错
//	//注释掉其中一个后编译器不会报错
//}
//6.动态开辟内存忘记释放（内存泄漏）
//void test()
//{
//	int* p = (int*)malloc(100);
//	if (NULL != p)
//	{
//		*p = 20;
//	}
//}//这里不会报错
// //但是不释放不会将内存还给系统，堆积消耗会占用内存
// //还会导致内存泄漏(test退出后，p就找不到了，再也无法释放
// //可是我们传入的数据还在内存中存储，但是不知道存储在哪里)
// //t:如果没有使用free来释放，当程序运行结束，操作系统也会回收
//int main()
//{
//	test();
//	while (1);
//}





//M1
//void GetMemory(char* p)//传入空指针
//{
//	p = (char*)malloc(100);
//}
//void Test(void)
//{
//	char* str = NULL;
//	GetMemory(str);
//	//上面这个函数相当于
//	//char* p = str;
//	//p = (char*)malloc(100);
//	//其实没有改变str，所以下面是对NULL解引用
//
//	strcpy(str, "hello world");		//str需要被解引用后才能复制字符串过去
//	//strcpy在string.h头文件中		//对NULL解引用，调试时程序崩溃
//	printf(str);
//}
//void GetMemory(char** p)//略作修改
//{
//	*p = (char*)malloc(100);
//}
//void Test(void)
//{
//	char* str = NULL;//指针(地址)str为空
//	GetMemory(&str);//&NULL
//	//上面这个函数相当于
//	//char** p = &str;
//	//*p = (char*)malloc(100);//也即str=(char*)malloc(100)
//	//p里面存的是str的地址，解引用后就是str
//
//	strcpy(str, "hello world\n");//
//	printf(str);
//
//	free(str);//动态开辟的内存，用完释放
//	str = NULL;
//
//	//下面举个例子
//	int a = 1;
//	int b = a;
//	b = 2;
//	printf("%d\n", a);//打印结果为1，表示没有对a的值进行改变
//	int* pa = &a;//p里面存放的是a的地址
//	*pa = 2;//p解引用后为a，*p=2也就是a=2
//	printf("%d\n", a);//打印结果为2，表示对a的值进行了改变
//}
//上面这个我感觉在举例了NULL不能解引用外，更多复习了指针传参的知识
//传入的是数据本身就是指针，不是传入数据的地址，容易绕晕，理解犯难
//这里可以把&a看做a,&&a看做&a


//M2
//char* GetMemory(void)
//{
//	char p[] = "hello world";
//	return p;
//	//return "hello world";
//	//这样返回数据就可以达到理想的效果，同时印证下面的推论
//}
//void Test(void)
//{
//	char* str = NULL;
//	str = GetMemory();//这样理解吧
//	//str=GetMemory()=p
//	//然后呢，在GetMemory()函数中p="hello world"，可是出了这个函数p这个地方存的是啥，你又不知道了
//	//p只在GetMemory函数中定义，外面没有这个数据，所以str等于啥不知道
//
//	//这样理解p的存储数据变化：
//	//1.开辟一块空间定义为p，把数据"hello world"存入进去
//	//2.函数结束后，p又重新归还系统，不存在了，但是在1中p的地址处存的还是"hello world"
//	//3.你返回p，确实不知道p这个地方放的什么数据
//	//4.总结就是，一个房子里面放着东西，本来这个房子叫1号房，函数结束前你可以去1号房找你想要的东西
//	//函数结束后，他就不叫1号房了，可能其他的房子叫1号房，你再去1号房你就找不到想找的东西了
//
//	printf(str);
//}


//M3
//void GetMemory(char** p, int num)
//{
//	*p = (char*)malloc(num);//num=100
//}
//void Test(void)
//{
//	char* str = NULL;
//	GetMemory(&str, 100);
//	strcpy(str, "hello");
//	printf(str);
//	free(str);//释放，系统运行结束会自动释放
//	str = NULL;
//}


//M4
//void Test(void)
//{
//	char* str = (char*)malloc(100);
//	strcpy(str, "hello");
//	free(str);
//	str = NULL;//？，没什么过于值得注意的地方
//	if (str != NULL)
//	{
//		strcpy(str, "world ");
//		printf(str);
//	}
//}

//int main()
//{
//	Test();
//	return 0;
//}




//柔性数组
//typedef struct S
//{
//	int a;
//	int arr[];//柔性数组成员
//	//arr[0]也符合
//}T;
////结构中的柔性数组成员前⾯必须⾄少⼀个其他成员。
////sizeof 返回的这种结构⼤⼩不包括柔性数组的内存。
////包含柔性数组成员的结构⽤malloc()函数进⾏内存的动态分配，并且分配的内存应该⼤于结构的⼤⼩，以适应柔性数组的预期⼤⼩。
//int main()
//{
//	printf("%d", sizeof(T));
//	T* p1 = (T*)malloc(sizeof(T) + 20 * sizeof(int));
//	//这样就相当于柔性数组arr[]获得了20个整型大小的空间
//	if (p1 == NULL)
//	{
//		perror("malloc");
//		return 1;
//	}
//	p1->a = 20;
//	int i = 0;
//	for (i = 0; i < 20; i++)
//	{
//		p1->arr[i] = i;
//	}
//	//上述为赋值
//
//	//调整空间
//	T* p2 = (T*)realloc(p1, sizeof(T) + 30 * sizeof(int));
//	if (p2 != NULL)
//	{
//		p1 = p2;
//	}
//
//	//释放空间
//	free(p1);
//	p1 = NULL;
//
//	return 0;
//}



